package com.winner;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 考察的都是线程间的通信问题
 * https://blog.csdn.net/qq36846776/article/details/110263246?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-4.queryctrv4&spm=1001.2101.3001.4242.3&utm_relevant_index=7
 *
 * 两个线程交替执行输出，一个数字1-5，一个字符a-e ,打印出来1a2b3c4d5e
 *
 * 关键字Synchronized与wait()和notify()/notifyAll()结合可以实现“等待/通知”模式，
 *
 * Lock类的子类ReentrantLock也可以实现同样的功能，但需要借助Condition对象。
 *
 * 优势：在一个Lock对象里面可以创建多个Condition（即对象监视器）实例，
 * 线程对象可以注册在指定的Condition中，从而可以有选择性地对指定线程进行通知，
 * 在调度线程上更加灵活。
 *
 */
public class ThreadJiaoTiDaYinDemo {

    public static volatile boolean flag = false;
    // volatile变量的原子性, 指的是写操作，原子性的特别总结为2点：对一个volatile变量的写操作，只有所有步骤完成，才能被其它线程读取到。
    // 多个线程对volatile变量的写操作本质上是有先后顺序的。也就是说并发写没有问题。


    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        // Condition是JUC包下提供的一个接口。 可以翻译成 条件对象，其作用是线程先等待，当外部满足某一条件时，在通过 条件对象 唤醒 等待的线程
        // signal() 唤醒等待的线程
        // await() 让线程进入等待，如果其他线程调用同一Condition对象的notify/notifyAll，那么等待的线程可能被唤醒
        Condition numCondition = lock.newCondition();
        Condition strCondition = lock.newCondition();
        new ThreadStr(lock,numCondition,strCondition).start();
        new ThreadNum(lock,numCondition,strCondition).start();
    }

    static class ThreadNum extends Thread {
        private Lock lock;
        private Condition numCondition;
        private Condition strCondition;

        public ThreadNum(Lock lock, Condition numCondition, Condition strCondition) {
            this.lock = lock;
            this.numCondition = numCondition;
            this.strCondition = strCondition;
        }

        @Override
        public void run() {
            lock.lock();
            flag = true;
            try {
                for (int i = 1; i <= 5; i++) {
                    System.out.print(i);
                    strCondition.signal();
                    numCondition.await();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
    static class ThreadStr extends Thread {
        private Lock lock;
        private Condition numCondition;
        private Condition strCondition;

        public ThreadStr(Lock lock, Condition numCondition, Condition strCondition) {
            this.lock = lock;
            this.numCondition = numCondition;
            this.strCondition = strCondition;
        }

        @Override
        public void run() {
            while (!flag) { // 这里一直是默认的false, 只有当上面的线程改为true,并打印 1 之后,唤醒了strCondition,即打印字母的线程,才可以打印字母
            }
            lock.lock();
            try {
                for (int i = 97; i < 102; i++) {
                    System.out.print((char) i);
                    numCondition.signal();
                    // 如果是e ,说明执行到最后了，lock.unlock(); 不在阻塞了
                    if(i!=101) {
                        strCondition.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

}
